/*
 * Agent.cpp
 *
 *  Created on: Jul 15, 2013
 *      Author: dsweeney
 */

#ifndef AGENT_H_
#define AGENT_H_

#include "Node.h"
#include "CommonTypes.h"
#include "LoggerSingleton.h"
#include <vector>
#include <algorithm>
#include <string>

#include <iostream>

class Agent
{
public:
	static Agent* instance(Node* dictionaryHead, char* rack);
	result_e findSolution(Node* n, int limit);
	void printRack();
	Agent (Node* dictionaryHead, char* rack);
	std::string getBestString();
	uint getBestScore();
	~Agent();
	void makePlay();
private:
	static Node* head;
	std::vector <char> rack;

	//heuristics
	uint g(Node*);
	uint h();
	uint f(Node*);
	void oneWildCard ();
	void twoWildCards ();
	void oneS ();
	void twoSs ();
	void tradeSquares();
	void placeWord();
	double sDeduction;
	double childDeduction;
	Node* best;
	uint bestScore;

	static bool sort (char a, char b);
	void combineQueues (std::vector <char*>, std::vector <char*>);
};
///////////////////////////////////////////////////////////////////////////////
Node* Agent::head = NULL;
///////////////////////////////////////////////////////////////////////////////
Agent::Agent (Node* dictionaryHead, char* newRack):
		sDeduction(1),
		childDeduction(.5),
		best(NULL),
		bestScore(0)
{
	std::string* sortedRack = new std::string();
	char completeSet[] = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
	std::vector<char> sortedAlphabet;

	this->head = dictionaryHead;

	for(int i = 0; isalpha(newRack[i]) || newRack[i] == '_'; i++)
	{
		this->rack.insert(this->rack.begin(), newRack[i]);
	}

	//sort the rack according to the heuristic
	std::sort(this->rack.begin(), this->rack.end(), (this->sort));
	this->rack.resize(7);
	std::vector<char>::iterator iter = rack.begin();
	sortedRack->append(&(*iter));
	sortedRack->resize(7);
	this->head->listOfChildren = sortedRack;

	//sort the alphabet according to the heuristic, and give to class Node (static)
	sortedAlphabet = std::vector<char> (completeSet,
			completeSet + sizeof(completeSet) / sizeof(char) );
	std::sort(sortedAlphabet.begin(), sortedAlphabet.end(), (this->sort));
	sortedAlphabet.resize(26);
	iter = sortedAlphabet.begin();
	Node::fullSet.append(&(*iter));
	Node::fullSet.resize(26);
}
///////////////////////////////////////////////////////////////////////////////
Agent::~Agent()
{

}
///////////////////////////////////////////////////////////////////////////////
bool Agent::sort(char a, char b)
//x has the least number of children
//s has the most
//hopefully by using the letters in order highest occurance, optimal solutions
//will come faster.
{
	uint valA, valB;

	valA = a!= '_'?
			Agent::head->getChild(a)->numWholeWordChildren():
			Agent::head->getChild('s')->numWholeWordChildren();
	valB = b!= '_'?
			Agent::head->getChild(b)->numWholeWordChildren():
			Agent::head->getChild('s')->numWholeWordChildren();

	return valA > valB;
}
///////////////////////////////////////////////////////////////////////////////
uint Agent::g(Node* n)
{
	//word score - wildcards, then accounting for bingo
	std::string* s = new std::string();
	const char* c = (this->head->listOfChildren->c_str());
	n->getString(s);
	uint score = 0;

	//only worrying about the first and fifth letter, running out of time....
	if(s->length() > 5)
	{
		if((Node::charToPoint[s->at(0)] >= Node::charToPoint[s->at(4)]) &&
			((int)(this->head->listOfChildren->find(s->at(0))) != -1))
			score = score + (Node::charToPoint[s->at(0)]);
		else if((Node::charToPoint[s->at(0)] < Node::charToPoint[s->at(4)]) &&
				((int)(this->head->listOfChildren->find(s->at(4))) != -1))
			score = score + (Node::charToPoint[s->at(4)]);
	}

	for(uint i = 0; i < this->head->listOfChildren->length(); i++)
	{
		int location = s->find(c[i]);

		if(location != -1)
		{
			score = score + Node::charToPoint[c[i]];
		}
	}

	delete s;
	return score;
}
///////////////////////////////////////////////////////////////////////////////
uint Agent::h()
//optimal score... not really
{
	static uint totalCost = 0;

	//only want this to run once
	if(totalCost == 0)
	{
		uint high = 0;
		for(uint i = 0; i < this->rack.size(); i++)
		{
			if(high <  Node::charToPoint[this->rack[i]])
				high = Node::charToPoint[this->rack[i]];

			totalCost += Node::charToPoint[this->rack[i]];
		}
		//accounting for double letter
		totalCost = totalCost + high;
	}
	return totalCost + 50;
}
///////////////////////////////////////////////////////////////////////////////
uint Agent::f(Node* n)
{
	//optimal score - actual score
	int returnVal = this->g(n) + (this->h() - n->getScore());

	return returnVal;
}
///////////////////////////////////////////////////////////////////////////////
result_e Agent::findSolution(Node* n, int limit)
//10 is the average word score (32 including bingo bonus) for first play 15 overall.
//x has the least number of children
{
	if(n->wholeWord == true)
	{

		uint score = this->g(n);

		if(best == NULL || (score > this->bestScore))
		{
			best = n;
			bestScore = score;
		}
	}

	if((n->wholeWord == true) && (this->h() <= n->getScore()))
	{
		//optimal score achieved
		best = n;
		return GOAL;
	}
	else if (limit == 0)
		return CUTOFF;
	else
	{
		bool cutoffOccurred = false;
		Node* child = NULL;
		while((child = n->getNextChild()) != NULL)
		{
			result_e result = this->findSolution(child, limit - 1);

			switch(result)
			{
			case CUTOFF:
				cutoffOccurred = true;
				break;
			case FAILURE:
				break;
			default:
				return result;
			};
		}

		if(cutoffOccurred)
			return CUTOFF;
		else
			return FAILURE;
	}
}
///////////////////////////////////////////////////////////////////////////////
std::string Agent::getBestString()
{
	std::string result;
	this->best->getString(&result);

	return result;
}
///////////////////////////////////////////////////////////////////////////////
uint Agent::getBestScore()
{
	return this->bestScore;
}
///////////////////////////////////////////////////////////////////////////////
void Agent::makePlay()
{
	std::string* s = this->head->listOfChildren; //alias
	int sS = 0, wc = 0, vowel = 0;
	uint length = s->length();

	for(uint i = 0; i < length; i++)
	{
		char c = s->at(i);

		switch(c)
		{
		case '_':
			wc++;
			break;
		case 's':
			sS++;
			break;
		case 'a':
		case 'e':
		case 'i':
		case 'o':
		case 'u':
			vowel++;
			break;
		default:
			break;
		};
	}

		if (wc == 2)
			this->twoWildCards();
		else if (wc == 1)
			this->oneWildCard();
		else if (sS > 1)
			this->twoSs();
		else
			//general solution
			this->oneS();

		if(this->best == NULL)
			this->tradeSquares();
		else
			this->placeWord();

}
///////////////////////////////////////////////////////////////////////////////
void Agent::oneWildCard()
{
	//1 wildcard if a bingo is possible with it use it. Because of the
	//way the tree is build easier to cycle through
	for(uint i = 0; i < this->head->listOfChildren->length(); i++)
	{
		std::string* copy;

		char c = this->head->listOfChildren->at(0);
		this->head->listOfChildren->erase(0, 1);
		this->head->listOfChildren->append(&c, 1);

		copy = new std::string(this->head->listOfChildren->c_str());

		this->findSolution(this->head, 7);

		this->head->reInit();
		head->listOfChildren = copy;
	}
}
///////////////////////////////////////////////////////////////////////////////
void Agent::twoWildCards()
//2 wildcards keep 1
{
	int location = this->head->listOfChildren->find('_');

	if(location != -1)
		this->head->listOfChildren->erase(location, 1);

	this->oneWildCard();
}
///////////////////////////////////////////////////////////////////////////////
void Agent::oneS()
//just try optimal solution
{
	this->findSolution(this->head, 7);
}
///////////////////////////////////////////////////////////////////////////////
void Agent::twoSs()
//see if this results in bingo otherwise keep one s
{
	this->findSolution(this->head, 7);

	//unless i can uses all the tile keep one s
	if(this->getBestString().length() == 7)
		return;

	int location = this->head->listOfChildren->find('s');

	if(location != -1)
		this->head->listOfChildren->erase(location, 1);

	this->findSolution(this->head, 7);
}
///////////////////////////////////////////////////////////////////////////////
void Agent::tradeSquares()
//trade the 2 highes values. they have less possible strings
{
	char lowest = this->head->listOfChildren->at(0);
	char second = this->head->listOfChildren->at(1);

	for(uint i = 2; i < this->head->listOfChildren->length(); i++)
	{
		if(Node::charToPoint[lowest] <
			Node::charToPoint[head->listOfChildren->at(i)])
			lowest = head->listOfChildren->at(i);
		else if(Node::charToPoint[second] <
			Node::charToPoint[head->listOfChildren->at(i)])
			second = head->listOfChildren->at(i);
	}
	std::cout<<"EXCHANGE "<<lowest<<" "<<second<<std::endl;
}
///////////////////////////////////////////////////////////////////////////////
void Agent::placeWord()
{
	std::string s = this->getBestString();
	int location = 0;

	if(s.length() > 5)
	{
		if((Node::charToPoint[s.at(0)] >= Node::charToPoint[s.at(4)]) &&
			((int)(this->head->listOfChildren->find(s.at(0))) != -1))
			location = 4;
		else if((Node::charToPoint[s.at(0)] < Node::charToPoint[s.at(4)]) &&
				((int)(this->head->listOfChildren->find(s.at(4))) != -1))
			location = 12;
	}
	else
		location = 8;

	std::cout<<"PLACE WORD "<<s<<" SCORE "<<this->g(this->best)*2<<" COLUMN "<<location<<std::endl;
}
#endif /* AGENT_CPP_ */
